﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Diggins.Jigsaw;


namespace Domain
{
    public class Evaluator
    {
        public StringBuilder builder { get; private set; }
        public StringBuilder blockBuilder { get; private set; }

        private string checkNum = "( ^ v => ^ m => ^ i => ^ o => isNum v then (v ! ( m ! i ! o ) ) else error )";
        private string checkBool = "( ^ v => ^ m => ^ i => ^ o => isBool v then (v ! ( m ! i ! o ) ) else error )";


        public Evaluator()
        {
            builder = new StringBuilder();
            blockBuilder = new StringBuilder();
        }
        public string Evaluate(Node n)
        {
            switch (n.Label)
            {
                case "Program":
                    {
                        builder.Append(String.Format("{0}", Evaluate(n[0])));
                        if (n.Count > 1)
                        {
                            for (int i = 1; i < n.Count; i++)
                            {
                                builder.Append("*");
                                builder.Append(Evaluate(n[i]));
                            }
                        }
                        return builder.ToString();
                    }
                case "VarDecl":
                    {
                        return String.Format("{0} * ( ^ v => ^ m => ^ i => ^ o => ( m[ {1} / v ] ! i ! o ) )", (Evaluate(n[1])), n[0].Text);
                    }
                case "output":
                    {
                        return String.Format("{0} * ( ^ v => ^ m => ^ i => ^ o => ( m ! i ! v & o ) )", Evaluate(n[0]));
                    }
                case "If":
                    {
                        Node uselessNode = n[0];
                        return String.Format("{0} * {3} * ( ^ v => ^ m => ^ i => ^ o => v = true then {1} else {2} )",
                                Evaluate(uselessNode[0]), Evaluate(n[1]), Evaluate(n[2]), checkBool);
                    }
                case "Else":
                    {
                        return Evaluate(n[0]);
                    }
                case "Block":
                    {

                        blockBuilder.Append(String.Format("( {0} )", Evaluate(n[0])));
                        if (n.Count > 1)
                        {
                            for (int i = 1; i < n.Count; i++)
                            {
                                blockBuilder.Append("*");
                                blockBuilder.Append(Evaluate(n[i]));
                            }
                        }
                        var result = blockBuilder.ToString();
                        blockBuilder.Clear();
                        return result;
                    }
                case "Expr":
                    {
                        switch (n[0].Label)
                        {
                            case "read":
                                {
                                    return "( ^ m => ^ i => ^ o => empty i then error else ( head i ! ( m ! tail i ! o ) ) )";
                                }
                            case "False":
                                return "( ^ m => ^ i => ^ o => ( false ! ( m ! i ! o ) ) )";
                            case "True":
                                return "( ^ m => ^ i => ^ o => ( true ! ( m ! i ! o ) ) ) ";
                            case "Integer":
                                return String.Format("( ^ m => ^ i => ^ o => ( {0} ! ( m ! i ! o ) ) )", n.Text);
                            case "BinaryExpr":
                                {
                                    switch (n[0][1].Text)
                                    {
                                        case "+":
                                            {
                                                return String.Format("( {0} * {2} * ( ^ v1 => {1} * {2} * ( ^ v2 => ( v1 + v2 ! ( m ! i ! o ) ) ) ) )",
                                                    Evaluate(n[0][0]), Evaluate(n[0][2]), checkNum);
                                            }
                                        case "=":
                                            {
                                                return String.Format("( {0} * ^ v1 => {1}  * ^ v2 => ( v1 = v2 ! ( m ! i ! o ) ) )",
                                                    Evaluate(n[0][0]), Evaluate(n[0][2]));
                                            }
                                        default: throw new Exception("unsupported binary operation");
                                    }
                                }
                            case "PrefixExpr":
                                {
                                    switch (n[0][0].Text)
                                    {
                                        case "not":
                                            {
                                                var uselessNode = n[0][1];
                                                var eval = Evaluate(uselessNode[0]);

                                                return String.Format("( {0} * {1} * ^ v => ( not v ! ( m ! i ! o ) ) )", eval, checkBool);
                                            }
                                        default: throw new Exception("unsupported unary operation");
                                    }
                                }
                            case "Identifier":
                                {
                                    return String.Format("( ^ m => ^ i => ^ o => m[{0}] = unbound then error else ( m[{0}] ! ( m ! i ! o ) ) )", n.Text);
                                }
                        }
                        break;
                    }
                case "Identifier":
                    {
                        return String.Format("( ^ m => ^ i => ^ o => m[{0}] = unbound then error else ( m[{0}] ! ( m ! i ! o ) ) )", n.Text);
                    }
                case "False":
                    return "( ^ m => ^ i => ^ o => ( false ! ( m ! i ! o ) ) )";
                case "True":
                    return "( ^ m => ^ i => ^ o => ( true ! ( m ! i ! o ) ) ) ";
                case "Integer":
                    return String.Format("( ^ m => ^ i => ^ o => ( {0} ! ( m ! i ! o ) ) )", n.Text);
                default: throw new Exception("unsupported comman or exprecion");
            }
            return "error";
        }

    }
}

